﻿using System;

namespace CommerceSampleCode
{
    /// <summary>
    /// This class provides properties to generate random credit card numbers
    /// </summary>
    public class CreditCardRandomizer
    {
        #region // Constructors/Desctructors

        /// <summary>
        /// Static constructor - empty
        /// </summary>
        static CreditCardRandomizer()
        {
        }

        #endregion

        #region // Properties

        /// <summary>
        /// Returns a random Visa credit card number
        /// </summary>
        static public String Visa
        {
            get
            {
                return getRandomCC(4, 16);
            }
        }

        /// <summary>
        /// Returns a random MasterCard credit card number
        /// </summary>
        static public String MasterCard
        {
            get
            {
                return getRandomCC(51, 16);
            }
        }

        /// <summary>
        /// Returns a random American Express credit card number
        /// </summary>
        static public String AmericanExpress
        {
            get
            {

                Int32 prefix = 0;
                switch (ThreadSafeRandom.Next(1, 3))
                {
                    case 1:
                        prefix = 34;
                        break;
                    case 2:
                        prefix = 37;
                        break;
                    default:
                        prefix = 34;
                        break;
                }

                return getRandomCC(prefix, 15);
            }
        }

        /// <summary>
        /// Returns a random Discover credit card number
        /// </summary>
        static public String Discover
        {
            get
            {
                return getRandomCC(6011, 16);
            }
        }

        /// <summary>
        /// Returns a random Diners credit card number
        /// </summary>
        static public String Diners
        {
            get
            {
                Int32 prefix = 0;
                switch (ThreadSafeRandom.Next(1, 5))
                {
                    case 1:
                        prefix = 30;
                        break;
                    case 2:
                        prefix = 36;
                        break;
                    case 3:
                        prefix = 38;
                        break;
                    case 4:
                        prefix = 39;
                        break;
                    default:
                        prefix = 30;
                        break;
                }

                return getRandomCC(prefix, 14);
            }
        }

        /// <summary>
        /// Returns a random KLCC credit card number
        /// </summary>
        static public String KLCC
        {
            get
            {
                Int32 prefix = 0;
                switch (ThreadSafeRandom.Next(1, 5))
                {
                    case 1:
                        prefix = 2;
                        break;
                    case 2:
                        prefix = 4;
                        break;
                    case 3:
                        prefix = 5;
                        break;
                    case 4:
                        prefix = 9;
                        break;
                    default:
                        prefix = 9;
                        break;
                }

                return getRandomCC(prefix, 16);
            }
        }

        /// <summary>
        /// Return s a consumer debit card number
        /// </summary>
        static public String ConsumerDebit
        {
            get
            {
                return getRandomCC(648200, 16);
            }
        }

        /// <summary>
        /// Return a BusinessDebit card number
        /// </summary>
        static public String BusinessDebit
        {
            get
            {
                return getRandomCC(648740, 16);
            }
        }

        /// <summary>
        /// Returns a random JCB credit card number
        /// </summary>
        static public String JCB
        {
            get
            {
                Int32 prefix = 0;

                prefix = ThreadSafeRandom.Next(3528, 3589);

                return getRandomCC(prefix, 16);
            }
        }

        #endregion

        #region // Methods

        /// <summary>
        /// Returns a random credit card number for the type of card provided
        /// </summary>
        /// <param name="creditCardType">The credit card type. Can be American Express, Discover, JCB, Mastercard, or Visa. Tries to handle abbreviations and name variations (eg: AMEX). Defaults to Visa.</param>
        /// <returns>A random credit card number in string format</returns>
        static public String GetRandomAccountNumber(String creditCardType)
        {
            String accountNumber;

            creditCardType = creditCardType.ToLower();

            switch (creditCardType)
            {
                case "amex":
                case "americanexpress":
                case "american express":
                    accountNumber = AmericanExpress;
                    break;
                case "discover":
                    accountNumber = Discover;
                    break;
                case "jcb":
                    accountNumber = JCB;
                    break;
                case "mc":
                case "mastercard":
                case "master card":
                    accountNumber = MasterCard;
                    break;
                case "visa":
                    accountNumber = Visa;
                    break;
                case "diners":
                    accountNumber = Diners;
                    break;
                case "klcc":
                    accountNumber = KLCC;
                    break;
                case "ConsumerDebit":
                    accountNumber = ConsumerDebit;
                    break;
                case "BusinessDebit":
                    accountNumber = BusinessDebit;
                    break;
                default:
                    accountNumber = Visa;
                    break;
            }

            return accountNumber;
        }

        /// <summary>
        /// Constructs the actual random credit card number. All credit cards follow the same checksum, the only difference being the prefix and length of the number
        /// </summary>
        /// <param name="prefix">The prefix of the credit card type. For example, Visa cards start with a 4</param>
        /// <param name="length">The length of the credit card type. For example, Visa cards are all 16 digits in length</param>
        /// <returns>A random credit card number in string format</returns>
        static public String getRandomCC(Int32 prefix, Int32 length)
        {
            String accountString;
            Int32 sumOfAllDigits = 0;

            // Use the prefix if provided
            if (prefix == 0)
                accountString = String.Empty;
            else
                accountString = prefix.ToString();

            // Create a random string until the last digit ( reserved for checksum )
            while (accountString.Length < (length - 1))
            {
                accountString = accountString + ThreadSafeRandom.Next(0, 9).ToString();
            }


            // Count up the string, 
            for (Int32 stringPosition = 0; stringPosition < accountString.Length; stringPosition++)
            {
                Int32 digit = Convert.ToInt32(accountString.Substring(stringPosition, 1));

                // Take the odd positions in the string and perform a checksum on them
                // Odd positions are the 1st, 3rd, 5th, etc., starting from the right
                // Even positioned numbers are used as is
                if (((accountString.Length - stringPosition) % 2) != 0)
                {
                    digit = digit * 2;

                    if (digit >= 10)
                        digit = digit - 9;
                }

                // Take the resulting number and sum it to the others.
                sumOfAllDigits = sumOfAllDigits + digit;
            }

            // Perform the checksum on the sum.
            sumOfAllDigits = (10 - (sumOfAllDigits % 10));

            if (sumOfAllDigits == 10)
                sumOfAllDigits = 0;

            // Append the checksum digit to the end of the string
            accountString = accountString + sumOfAllDigits.ToString();

            // Return the random credit card number
            return accountString;
        }

        #endregion

    }
}
